# -*- coding: utf-8 -*-
from clasesPrincipales.CPU import CPU
from clasesPrincipales.ManagerInterruptor import ManagerInterruptor, Signal
from clasesPrincipales.PCB import PCB
from estados.Estados import Ready
from memoriaFisica.IO import IO
from planificacion.Scheduler import Scheduler
import threading
import time

class Kernel:
    
###############################################################################################################

    #INICIALIZACION
    
    ###variables    
    pid_counter = 0 #contador, q asigna id a los porcesos
    lock = threading.RLock() # semaforo
 
    ###constructor
    def __init__(self, aPolitic, mmu):
        self.modekernel = False
        self.running = True
        self.io = IO()
        self.mmu = mmu
        self.mmu.setIO(self.io)
        #Arranco cpu
        self.cpu = CPU(mmu=self.mmu, kernel=self)
        self.cpu.start()
        #manager
        self.managerInterruptor = ManagerInterruptor(self)
        # el scheduler
        self.sch = Scheduler(aPolitic, self.managerInterruptor)
  
###############################################################################################################

    #METODOS
    
    # CONTROL 
 
    def stop(self):
        # Detiene a todos los threads, para esto, seteo su bandera a false
        while self.cpu.pc is not None:
            time.sleep(1)
        self.running = False  
        
    # WORK 
        #---------------------------------------------------------
    def crearPCB(self, aProgram):
        print('* Se creo pbc del programa: %s'% aProgram.getName())
        print ('Instrucciones: %s' % aProgram.getCodigo())
        pid = self.pid_counter
        self.incrementar_pidCounter()
        aTask = PCB(aProgram, pid)  
        aTask.setState(Ready())   
        self.mmu.loadProgram(aTask)
        self.sch.pcbt[pid] = aTask  #Crea un pcb a partir de la llegada de un programa
                                    #guardandolo en la pcbt 
        self.sch.addTask(aTask) # agrego el pcb a la cola de listos
   
    def leerPrograma (self, aProgram):
        self.lock.acquire() # adquiero el semaforo, para evitar problemas en la ejecucion
        self.modekernel = True
        try:
            self.crearPCB(aProgram)
        finally:                                         
            self.modekernel = False
            self.lock.release() # libero el semaforo 
        print ('Se esta leyendo el programa' )
        self.managerInterruptor.interrupcion(Signal.NEW)


    def contextSwich(self):
        if self.cpu.pc is not None: # Guardo el contexto del ultimo proceso en ejecucion
            self.sch.pcbt[self.cpu.current_pid] = self.cpu.pc

    def reschedule(self):
        self.contextSwich()
        if self.sch.hasNext():
            temporal =  self.sch.nextTask()
            self.mmu.ponerListo(temporal)
            self.cpu.current_pid =temporal.getId()
            self.cpu.pc = temporal   
    
    def entradaSalida(self): # do nothing by now  
        temporal = self.sch.pcbt[self.cpu.current_pid]
        temporal.aumentarInstruction()
        self.sch.pcbt[self.cpu.current_pid] = temporal
        self.sch.addTask(temporal)
        
    def salir (self):
        #limpia el sistema de un proceso
        self.cpu.pc = None #vacio el pc de la cpu
        pcb = self.sch.pcbt[self.cpu.current_pid] # limpio la tabla de procesos
        del self.sch.pcbt[self.cpu.current_pid]
        self.mmu.unload(pcb) #limpio la memoria
        self.cpu.current_pid = None #reseteo el pid
###############################################################################################################

    #GET&SET       
    def getRunning (self):
        return self.running
    def getReady(self):
        return self.ready
    def getKernelMode(self):
        return self.modekernel
    def incrementar_pidCounter (self):
        self.pid_counter += 1
    def getScheduler(self):
        return self.sch
   
        